package com.vogtec.fota.httpclient;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.MultiPartContentProvider;
import org.eclipse.jetty.client.util.PathContentProvider;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class HttpClientServiceImpl implements HttpClientService{

	private static final Logger log = LoggerFactory.getLogger(HttpClientServiceImpl.class);

	private HttpClient httpClient;

	public HttpClientServiceImpl(){
		httpClient = new HttpClient();
		try {
			httpClient.start();
		} catch (Exception e) {
			log.error("", e);
		}
	}

	public HttpClientServiceImpl(SslContextFactory sslContextFactory){
		if(sslContextFactory != null){
			httpClient = new HttpClient(sslContextFactory);

			SSLContext sslContext = sslContextFactory.getSslContext();
			if(sslContext != null) {
				log.debug("SSLContent protocol: " + sslContext.getProtocol());
				log.debug("SSLContext provider: " + sslContext.getProvider().getName());
				log.debug("Include Cipher suites: " + Arrays.asList(sslContextFactory.getIncludeCipherSuites()));
				log.debug("Exclude Cipher suites:" + Arrays.asList(sslContextFactory.getExcludeCipherSuites()));
				log.debug("Selected Cipher suites: " + Arrays.asList(sslContextFactory.getSelectedCipherSuites()));
			}
		}
		else{
			httpClient = new HttpClient();
		}

		try {
			httpClient.start();
		} catch (Exception e) {
			log.error("", e);
		}
	}

	public void destroy(){
		try {
			if(httpClient != null)
				httpClient.stop();
		} catch (Exception e) {
			log.error("", e);
		}
	}

	/**
	 * 日志输出HTTP Request
	 * @param request
	 * @param bodyStr 如果请求有String类型的ContentBody，则可以传入用于日志输出，否则传入null
	 * @return
	 */
	private String requestStr(Request request, String bodyStr){
		StringBuffer sb = new StringBuffer("\n");
		
		sb.append(request.getMethod()).append(" ");
		sb.append(request.getURI()).append(" ");
		sb.append(request.getVersion()).append("\n");

		for(HttpField field : request.getHeaders())
			sb.append(field.toString()).append("\n");
		
		if(bodyStr != null)
			sb.append("\n").append(bodyStr).append("\n");
			
		return sb.toString();
	}
	
	/**
	 * 日志输出HTTP Response
	 * @param response
	 * @return
	 */
	private String responseStr(ContentResponse response){

		StringBuffer sb = new StringBuffer("\n");
		
		sb.append(response.getVersion()).append(" ");
		sb.append(response.getStatus()).append(" ");
		sb.append(response.getReason()).append("\n");

		for(HttpField field : response.getHeaders())
			sb.append(field.toString()).append("\n");
		
		if(response.getContentAsString() != null)
			sb.append("\n").append(response.getContentAsString()).append("\n");
		
		return sb.toString();
	}

	@Override
	public Request OPTIONS(String uri) throws IllegalArgumentException {
		Request options = httpClient.newRequest(uri).method(HttpMethod.OPTIONS);

		log.debug(requestStr(options, null));
		
		return options;
	}

	@Override
	public Request GET(String uri) throws IllegalArgumentException {
		Request get = httpClient.newRequest(uri);

		log.debug(requestStr(get, null));
		
		return get;
	}

	@Override
	public Request PUT(String uri, String contentType, String body) throws IllegalArgumentException {
		if(contentType == null && body != null){
			log.warn("Param contentType is NULL, set to default text/plain");
			contentType = "text/plain";
		}

		Request put = httpClient.newRequest(uri).method(HttpMethod.PUT);
		
		if(body != null)
			put.content(new StringContentProvider(body), contentType);
		
		log.debug(requestStr(put, body));
		
		return put;
	}

	@Override
	public Request DELETE(String uri) throws IllegalArgumentException {
		Request delete = httpClient.newRequest(uri).method(HttpMethod.DELETE);

		log.debug(requestStr(delete, null));
		
		return delete;
	}
	
	@Override
	public Request POST(String uri, String contentType, String body) throws IllegalArgumentException {
		if(contentType == null && body != null){
			log.warn("Param contentType is NULL, set to default text/plain");
			contentType = "text/plain";
		}

		Request post = httpClient.newRequest(uri).method(HttpMethod.POST);

		if(body != null)
			post.content(new StringContentProvider(body), contentType);
		
		log.debug(requestStr(post, body));
		
		return post;
	}

	@Override
	public Request POSTFile(String uri, File file) throws IllegalArgumentException, IOException {
		if(file == null || !file.canRead())
			throw new FileNotFoundException(((file == null) ? "null" : file.getAbsolutePath()));
		
		MultiPartContentProvider multiPart = new MultiPartContentProvider();
		multiPart.addFilePart("file", file.getName(), new PathContentProvider(Paths.get(file.toURI())), null);
		multiPart.close();

		Request post = httpClient.POST(uri).content(multiPart);

		log.debug(requestStr(post, null));
		
		return post;
	}
	
	@Override
	public Request listener(Request request, Request.Listener.Adapter asynListener) {
		return request.listener(asynListener);
	}

	@Override
	public ContentResponse send(Request request, int timeout) throws TimeoutException, IOException {
				
		ContentResponse response = null;
		
		try {
			response = request.timeout(timeout, TimeUnit.SECONDS).send();
		} catch (InterruptedException | ExecutionException e) {
			log.error("", e);
		}
		
		log.debug(responseStr(response));
		
		return response;
	}

	@Override
	public void send(Request request, Response.Listener.Adapter asynListener) {
		request.send(asynListener);
	}
}
